﻿using Ec.NETCoreFramework.Common;
using Ec.NETCoreFramework.Common.Core;
using Ec.NETCoreFramework.Common.Core.Helpers;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Ec.NETCoreFramework.Common.Cache
{
    /// <summary>
    /// 单redis 锁
    /// </summary>
    public class RedisSingleLock : IRedisSingleLock
    {
        /// <summary>
        /// database
        /// </summary>
        IDatabase _Database;

        static readonly object syncObj = new object();
        private static ConnectionMultiplexer _instance = null;
        /// <summary>
        /// Initializes a new instance of the <see cref="RedisSingleLock"/> class.
        /// </summary>
        /// <param name="server">服务器信息.</param>
        /// <param name="password">密码.</param>
        /// <param name="dbId">数据库Id.</param>
        public RedisSingleLock(string server, string password, int dbId)
        {
            try
            {
                ConfigurationOptions option = new ConfigurationOptions
                {
                    AbortOnConnectFail = false,
                    ConnectTimeout = 15000,
                    EndPoints = { server }
                };
                if (!string.IsNullOrEmpty(password))
                {
                    option.Password = password;
                }
                GetMultiplexer(server, password);
                if (_instance != null && _instance.IsConnected)
                {
                    _Database = _instance.GetDatabase(dbId);
                }
            }
            catch (Exception ex)
            {
                LoggerHelper.Instance.Error("Cache-Redis" + ex.Message);
            }
        }
        private ConnectionMultiplexer GetMultiplexer(string server, string password = null)
        {
            if (_instance == null || !_instance.IsConnected)
            {
                lock (syncObj)
                {
                    if (_instance == null || !_instance.IsConnected)
                    {
                        ConfigurationOptions option = new ConfigurationOptions
                        {
                            AbortOnConnectFail = false,
                            ConnectTimeout = 15000,
                            EndPoints = { server }
                        };
                        if (!string.IsNullOrEmpty(password))
                        {
                            option.Password = password;
                        }
                        _instance = ConnectionMultiplexer.Connect(option);
                    }
                }
            }
            return _instance;
        }
        /// <summary>
        /// 加锁
        /// </summary>
        /// <param name="key">锁定的key值</param>
        /// <param name="value">锁定的value值</param>
        /// <param name="expiryTime">锁定过期时间，锁区域内的逻辑执行如果超过过期时间，锁将被释放</param>
        /// <param name="waitTime">等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间</param>
        /// <param name="loops">循环次数</param>
        /// <returns></returns>
        public bool Lock(string key, string value, TimeSpan expiryTime, TimeSpan waitTime, int loops)
        {
            var flag = false;
            while (loops > 0)
            {
                flag = _Database.LockTake(key, value, expiryTime);

                //加锁成功，则退出循环
                if (flag)
                {
                    break;
                }
                loops--;
                // 加锁失败，线程休眠下，走循环，再尝试加锁
                Thread.Sleep(waitTime);
            }
            return flag;
        }
        /// <summary>
        /// 2、解锁
        /// </summary>
        public void UnLock(string key, string value, int loops)
        {
            bool flag = _Database.LockRelease(key, value);
            while (loops > 0)
            {
                //如果解锁成功，则退出循环
                if (flag)
                {
                    break;
                }
                loops--;
                // 解锁失败，线程休眠下，走循环，再尝试解锁
                Thread.Sleep(10);
            }
        }
        /// <summary>
        /// redis 锁
        /// </summary>
        /// <param name="key">锁定的key值</param>
        /// <param name="expiryTime">锁定过期时间，锁区域内的逻辑执行如果超过过期时间，锁将被释放</param>
        /// <param name="waitTime">等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间</param>
        /// <param name="func">回调函数</param>
        /// <param name="loops">循环次数</param>
        /// <returns>IResponse</returns>
        public async Task<IResponse> BlockingWork(string key, TimeSpan expiryTime, TimeSpan waitTime, Func<Task<IResponse>> func, int loops = 3)
        {
            var flag = this.Lock(key, key, expiryTime, waitTime, loops);
            if (flag)
            {
                var obj= await func();
                this.UnLock(key, key, loops);
                return obj;
            }
            return FrameworkResponse.SystemError("获取锁失败");
        }
    }
}
